package code.google.jcustomize;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.FunctionMapper;
import javax.servlet.jsp.el.VariableResolver;

import org.apache.commons.el.ExpressionEvaluatorImpl;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

class ResultSerializer {
    private abstract static class Result {
        protected String value;
        
        public void setValue(String value) {
            this.value = value;
        }
        
        public String getValue() {
            return value;
        }
        
        public abstract void write(Writer w) throws IOException;
    }
    
    private static class ValueResult extends Result {
        public void write(Writer w) throws IOException {
            w.write(getValue() + "\n");
        }
    }
    
    private static class PropertyResult extends Result {
        private String name;
        
        public void setName(String name) {
            this.name = name;
        }
        
        public void write(Writer w) throws IOException {
            w.write(name + "=" + getValue() + "\n");
        }
    }
    
    private List results = new ArrayList();
    private String path;
    private String encoding = "utf8";
    
    public void setPath(String path) {
        if ("".equals(path)) throw new IllegalArgumentException("Empty 'path' attribute");
        this.path = path;
    }
    
    public void setEncoding(String encoding) {
        if (!"".equals(encoding))
            this.encoding = encoding;
    }
    
    public void parse(Element output) {
        setPath(output.getAttribute("path"));
        setEncoding(output.getAttribute("encoding"));
        
        for (Node child = output.getFirstChild(); child != null; child = child.getNextSibling()) {
            if (child.getNodeType() != Node.ELEMENT_NODE) continue;
            Result result = null;
            if ("property".equalsIgnoreCase(child.getNodeName())) {
                PropertyResult pr = new PropertyResult();
                pr.setName(((Element)child).getAttribute("name"));
                result = pr;
            }
            else if ("value".equalsIgnoreCase(child.getNodeName())) {
                result = new ValueResult();
            }
            if (result != null) {
                result.setValue(AbstractConfig.getElementValue(child));
                results.add(result);
            }
        }
    }
    
    public boolean write(ConfigSource cs, VariableResolver vr, FunctionMapper fm) {
        if (results.isEmpty()) return true;
        OutputStream os = null;
        Writer w = null;
        try {
            os = cs.fileConfigWrite(path, false, false);
            w = new OutputStreamWriter(os, encoding);
            ExpressionEvaluator ee = new ExpressionEvaluatorImpl();
            for (Iterator it = results.iterator(); it.hasNext(); ) {
                Result r = (Result)it.next();
                String value = r.getValue();
                if (value == null)
                    continue;
                if (value.indexOf("${") >= 0)
                    r.setValue(value = (String)ee.evaluate(value, String.class, vr, fm));
                r.write(w);
            }
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ELException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (w != null)  try { w.flush();  } catch (IOException ioe) {}
            if (os != null) try { os.close(); } catch (IOException ioe) {}
        }
        return false;
    }
}
